//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_UNI_STRING_TYPE_H
#define _UXS_UNI_STRING_TYPE_H

#pragma once

#include "TUniArray.h"

namespace UX_STL
{
namespace UXTypes
{

namespace StringUtils
{
	
	template <typename T>
	inline T ToUpper(const T &c);
	
	template <typename T>
	inline T ToLower(const T &c);
	
	template <typename T>
	inline usize Length(const T *str)
	{
		usize	i = 0;

		while ( *str != 0 )
		{
			++str;
			++i;
		}
		return i;
	}


	template <>
	inline usize Length(const char *str)
	{
		return _hidden_::strlen( str );
	}

}	// StringUtils

	
	template <typename T>	struct TUniString;

	typedef TUniString< char >			uni_string;
	typedef TUniString< wchar >			uni_wstring;

	typedef TUniString< const char >	uni_c_string;
	typedef TUniString< const wchar >	uni_c_wstring;



	//
	// Universal String
	//

	template <typename T>
	struct TUniString
	{
	// types
	public:
		typedef TUniString<T>		Self;
		typedef TMemoryViewer<T>	vis_mem_t;
		typedef T					value_t;

		typedef uni_array<T>		uni_arr_t;
		typedef uni_array<const T>	uni_c_arr_t;

		typedef typename TypeTraits::InvertConst<T>::type	T_inv;

		typedef TUniString<T_inv>	inv_str_t;


	// variables
	private:
		union {
			T		*	_pElements;
			vis_mem_t	_pVisible;
		};
		usize			_uCount;


	// methods
	public:
		TUniString ();
		TUniString (T *pString);
		TUniString (T *pString, usize uLength);
		TUniString (void *pBegin, void *pEnd);

		T		&	operator [] (usize i);
		T const	&	operator [] (usize i) const;
		
		bool		operator == (const Self &sStr) const;
		bool		operator != (const Self &sStr) const;
		bool		operator == (const T *pStr) const;
		bool		operator != (const T *pStr) const;
		
		friend bool	operator == (const T * pStr, const Self &sStr)	{ return sStr == pStr; }
		friend bool	operator != (const T * pStr, const Self &sStr)	{ return sStr != pStr; }
		
		bool		operator ! () const			{ return not Empty(); }
		
		bool		operator <  (const Self &sStr) const;
		bool		operator >  (const Self &sStr) const;
		bool		operator <= (const Self &sStr) const;
		bool		operator >= (const Self &sStr) const;
		
		friend bool	operator <  (const T * pStr, const Self &sStr)	{ return Self(pStr) < sStr; }
		friend bool	operator >  (const T * pStr, const Self &sStr)	{ return Self(pStr) > sStr; }
		friend bool	operator <= (const T * pStr, const Self &sStr)	{ return Self(pStr) <= sStr; }
		friend bool	operator >= (const T * pStr, const Self &sStr)	{ return Self(pStr) >= sStr; }

		bool		NumStrLess (const Self &sStr) const;

		operator		uni_arr_t ()				{ return uni_arr_t( _pElements, _uCount ); }
		operator const	uni_c_arr_t ()		const	{ return uni_c_arr_t( _pElements, _uCount ); }
		operator const	inv_str_t ()		const	{ return inv_str_t( (T_inv *)_pElements, Length() ); }

		const TUniString<T_inv>	GetInv ()	const	{ return (TUniString<T_inv>)(*this); }

		T		*	ptr ();
		T const	*	ptr ()					const;
		T const	*	cstr ()					const	{ return ptr(); }
		
		T		&	Back ()							{ return (*this)[ Length()-1 ]; }
		T const	&	Back ()					const	{ return (*this)[ Length()-1 ]; }
		T		&	Front ()						{ return *ptr(); }
		T const	&	Front ()				const	{ return *ptr(); }

		usize		Length ()				const	{ return _uCount-1; }
		usize		Count ()				const	{ return _uCount; }
		usize		Size ()					const	{ return sizeof(T)*_uCount; }

		bool		Empty ()				const	{ return _uCount <= 1; }
		
		T		*	Begin ()						{ return ptr(); }
		T const	*	Begin ()				const	{ return ptr(); }
		T		*	End ()							{ return ptr() + Length(); }
		T const	*	End ()					const	{ return ptr() + Length(); }

		
		usize		GetIndex (const T &elem) const	{ return uni_c_arr_t(*this).GetIndex( elem ); }

		bool		Compare (const Self &sStr, usize uBegin, usize uEnd) const;
		bool		ICmpExt (const Self &sStr, usize uBegin, usize uEnd) const;
		bool		ICmp (const Self &sStr) const;
		bool		CmpPrefix (const Self &sStr, usize uLength = 0) const;
		
		bool Find (const TUniString<const T> &sStr, OUT usize &uPos, usize uStart = 0)	const;
		bool Find (const T tElem, OUT usize &uPos, usize uStart = 0)					const;

		bool FindI (const TUniString<const T> &sStr, OUT usize &uPos, usize uStart = 0)	const;
		bool FindI (const T tElem, OUT usize &uPos, usize uStart = 0)					const;

		TUniString<T>		SubString (usize uPos)							{ return SubString( uPos, Length()-uPos ); }
		TUniString<const T>	SubString (usize uPos)					const	{ return SubString( uPos, Length()-uPos ); }

		TUniString<T>		SubString (usize uPos, usize uCount);
		TUniString<const T>	SubString (usize uPos, usize uCount)	const;

		static bool s_Cmp (const T *pStr1, const T *pStr2, usize uLength);
	};



	
	template <typename T>
	inline TUniString<T>::TUniString ():
		_pElements(null), _uCount(0)
	{}
	
	
	template <typename T>
	inline TUniString<T>::TUniString (T *pString):
		_pElements(pString), _uCount( pString == null ? 0 : StringUtils::Length(pString)+1 )
	{}


	template <typename T>
	inline TUniString<T>::TUniString (T *pString, usize uLength):
		_pElements(pString), _uCount( pString == null ? 0 : uLength+1 )
	{}

	
	template <typename T>
	inline TUniString<T>::TUniString (void *pBegin, void *pEnd):
		_pElements( (T*)pBegin ), _uCount( ( usize(pEnd) - usize(pBegin) ) / sizeof(T) )
	{}

	
	template <typename T>
	inline T & TUniString<T>::operator [] (usize i)
	{
		ASSUME( i < _uCount );
		return ptr()[i];
	}
	

	template <typename T>
	inline T const & TUniString<T>::operator [] (usize i) const
	{
		ASSUME( i < _uCount );
		return ptr()[i];
	}
		
	
	template <typename T>
	inline bool TUniString<T>::operator == (const Self &sStr) const
	{
		if ( Length() != sStr.Length() )
			return false;

		if ( sStr.ptr() == _pElements or _uCount == 0 )
			return true;

		return s_Cmp( _pElements, sStr.ptr(), Length() );
	}

		
	template <typename T>
	inline bool TUniString<T>::operator == (const T *pStr) const
	{
		return ( *this == Self(pStr) );
	}

	
	template <typename T>
	inline bool TUniString<T>::operator != (const Self &sStr) const
	{
		return not ( *this == sStr );
	}

	
	template <typename T>
	inline bool TUniString<T>::operator != (const T *pStr) const
	{
		return not ( *this == pStr );
	}
		

	template <typename T>
	inline bool TUniString<T>::operator < (const Self &sStr) const
	{
		typedef typename TypeDescriptor::GetTypeInfo<T>::type::Similar_Unsigned		UType;

		if ( Length() != sStr.Length() )
			return Length() < sStr.Length();

		for (usize i = 0; i < sStr.Length(); ++i)
		{
			if ( (*this)[i] != sStr[i] )
				return UType( (*this)[i] ) < UType( sStr[i] );
		}
		return false;
	}
		

	template <typename T>
	inline bool TUniString<T>::operator > (const Self &sStr) const
	{
		typedef typename TypeDescriptor::GetTypeInfo<T>::type::Similar_Unsigned		UType;

		if ( Length() != sStr.Length() )
			return Length() > sStr.Length();

		for (usize i = 0; i < sStr.Length(); ++i)
		{
			if ( (*this)[i] != sStr[i] )
				return UType( (*this)[i] ) > UType( sStr[i] );
		}
		return false;
	}
	
		
	template <typename T>
	inline bool TUniString<T>::operator <= (const Self &sStr) const
	{
		if ( Length() > sStr.Length() )
			return false;

		return ( *this < sStr or *this == sStr );
	}
		

	template <typename T>
	inline bool TUniString<T>::operator >= (const Self &sStr) const
	{
		if ( Length() < sStr.Length() )
			return false;

		return ( *this > sStr or *this == sStr );
	}

	
	template <typename T>
	inline bool TUniString<T>::NumStrLess (const Self &sStr) const
	{
		typedef typename TypeInfo<T>::Similar_Uint	UType;

		for (usize l = 0, r = 0; l < Length() or r < sStr.Length(); ++l, ++r)
		{
			if ( (*this)[l] != sStr[r] )
				return UType( (*this)[l] ) < UType( sStr[r] );
		
			if ( (*this)[l] >= T('0') and (*this)[l] <= T('9') )
			{
				while ( (*this)[l] == T('0') ) { ++l; }
				while ( sStr[r] == T('0') ) { ++r; }

				const usize	l_begin = l,
							r_begin = r;

				while ( ((*this)[l] >= T('0') and (*this)[l] <= T('9')) and
						(sStr[r] >= T('0') and sStr[r] <= T('9') ) )
				{
					++l; ++r;
				}

				if ( (l - l_begin) == (r - r_begin) )
				{
					const usize	l_end = l,
								r_end = r;
					l = l_begin;
					r = r_begin;

					while ( l < l_end and r < r_end )
					{
						if ( (*this)[l] != sStr[r] )
							return UType( (*this)[l] ) < UType( sStr[r] );

						++l; ++r;
					}
				}
				else
					return ( (l - l_begin) < (r - r_begin) );
			}
		}
		return Length() < sStr.Length();
	}


	template <typename T>
	inline T * TUniString<T>::ptr ()
	{
		ASSUME( _pElements != null );
		return _pElements;
	}
	

	template <typename T>
	inline T const * TUniString<T>::ptr () const
	{
		ASSUME( _pElements != null );
		return _pElements;
	}

		
	template <typename T>
	inline bool TUniString<T>::CmpPrefix (const TUniString<T> &sStr, usize uLength) const
	{
		if ( uLength == 0 )
			uLength = sStr.Length();

		if ( Length() < uLength or sStr.Length() < uLength )
			return false;

		return s_Cmp( ptr(), sStr.ptr(), uLength );
	}


	template <typename T>
	inline bool TUniString<T>::s_Cmp (const T *pStr1, const T *pStr2, usize uLength)
	{
		for (usize i = 0; i < uLength; ++i) {
			if ( pStr1[i] != pStr2[i] )
				return false;
		}
		return true;
	}
	

	template <typename T>
	inline bool  TUniString<T>::Compare (const Self &sStr, usize uBegin, usize uEnd) const
	{
		if ( uBegin < uEnd or Length() <= uEnd or sStr.Length() <= uEnd ) {
			WARNING( "invalid args" );
			return false;
		}

		for (usize i = uBegin; i < uEnd; ++i) {
			if ( _pElements[i] != sStr._pElements[i] )
				return false;
		}
		return true;
	}
	

	template <typename T>
	inline bool  TUniString<T>::ICmp (const Self &sStr) const
	{
		if ( sStr.Length() != Length() )
			return false;

		return ICmpExt( sStr, 0, Length() );
	}


	template <typename T>
	inline bool  TUniString<T>::ICmpExt (const Self &sStr, usize uBegin, usize uEnd) const
	{
		// == stricmp

		if ( uBegin >= uEnd and Length() > uEnd and sStr.Length() > uEnd ) {
			WARNING( "invalid args" );
			return false;
		}
		
		for (usize i = uBegin; i < uEnd; ++i) {
			if ( StringUtils::ToLower( _pElements[i] ) != StringUtils::ToLower( sStr._pElements[i] ) )
				return false;
		}
		return true;
	}
	


	template <typename T>
	inline bool  TUniString<T>::Find (const TUniString<const T> &sStr, usize &uPos, usize uStart) const
	{
		if ( Empty() or sStr.Empty() )
			return false;

		usize	j = 0;

		for (usize i = uStart; i < _uCount-2; ++i)
		{
			while ( sStr[j] == _pElements[i+j] and i+j < _uCount-1 and j < sStr.Length() )
			{
				++j;
				if ( j >= sStr.Length() ) {
					uPos = i;
					return true;
				}
			}
			j = 0;
		}
		return false;
	}


	template <typename T>
	inline bool  TUniString<T>::Find (const T tValue, usize &uPos, usize uStart) const
	{
		if ( Empty() )
			return false;

		for (usize i = uStart; i < _uCount-2; ++i)
		{
			if ( _pElements[i] == tValue ) {
				uPos = i;
				return true;
			}
		}
		return false;
	}
	


	template <typename T>
	inline bool  TUniString<T>::FindI (const TUniString<const T> &sStr, usize &uPos, usize uStart) const
	{
		if ( Empty() or sStr.Empty() )
			return false;

		usize	j = 0;

		for (usize i = uStart; i < _uCount-2; ++i)
		{
			while ( StringUtils::ToLower( sStr[j] ) == StringUtils::ToLower( _pElements[i+j] ) and
					i+j < _uCount-1 and j < sStr.Length() )
			{
				++j;
				if ( j >= sStr.Length() ) {
					uPos = i;
					return true;
				}
			}
			j = 0;
		}
		return false;
	}


	template <typename T>
	inline bool  TUniString<T>::FindI (const T tValue, usize &uPos, usize uStart) const
	{
		if ( Empty() )
			return false;

		const T	val = StringUtils::ToLower( tValue );

		for (usize i = uStart; i < _uCount-2; ++i)
		{
			if ( StringUtils::ToLower( _pElements[i] ) == val ) {
				uPos = i;
				return true;
			}
		}
		return false;
	}


	template <typename T>
	inline TUniString<T>  TUniString<T>::SubString (usize uPos, usize uCount)
	{
		if ( uPos >= _uCount )
			return TUniString<T>();

		if ( uCount + uPos >= _uCount )
			uCount = _uCount - uPos - 1;

		return ( TUniString<T>( _pElements + uPos, uCount ) );
	}


	template <typename T>
	inline TUniString<const T>  TUniString<T>::SubString (usize uPos, usize uCount) const
	{
		if ( uPos >= _uCount )
			return TUniString<const T>();

		if ( uCount + uPos >= _uCount )
			uCount = _uCount - uPos - 1;

		return ( TUniString<const T>( _pElements + uPos, uCount ) );
	}

//-------------------------------------------------------------------

}	// UXTypes
}	// UX_STL

#endif	// _UXS_UNI_STRING_TYPE_H